using System;
using System.Collections.Generic;
using System.Text;

namespace LoRel {

    
    // Our Stateful Cartesian Product! Look how fucking simple it was at the end!
    // This could be n dimensional, but I think I put this on JoinClauseMatchSet instead, with this always
    // being a pair. Honestly, I don't fucking know. This shit was stupidly confusing.
    // its also not complete, there's really more shit.
    public class UnderlyingExclusiveSubset {
        
        public List< SubsetKey > SubsetKeys;

        private readonly List< List< TableRow > > m_UnderlyingRowsByBucketIndex;

        private CombinedKeySchema m_Schema;
        private readonly int m_ExclusiveIndex;
        private BucketTransaction m_AllAssociatedBuckets;

        public UnderlyingExclusiveSubset( CombinedKeySchema schema, 
                                          int exclusive_index,
                                          BucketTransaction all_associated_buckets)
        { 
            m_Schema = schema;
            m_ExclusiveIndex = exclusive_index;
            m_AllAssociatedBuckets = all_associated_buckets;
            SubsetKeys = new List<SubsetKey>();
            m_UnderlyingRowsByBucketIndex = new List<List<TableRow>>();
            
            for (int i = 0; i < all_associated_buckets.Count; i++ ) {
                m_UnderlyingRowsByBucketIndex.Add(new List<TableRow>());
            }

            if( all_associated_buckets.IsComplete == false ) {
                all_associated_buckets.BucketComplete += delegate( BucketTransaction transaction ) { WireBuckets(); };
            }else{ WireBuckets(); }
            
        }
        private void WireBuckets() {
            for( int i=0 ; i < m_AllAssociatedBuckets.Count ; i++ ) {

                if (m_ExclusiveIndex == i) continue;
                
                IndexBucket bucket = m_AllAssociatedBuckets[ i ];
                int i1 = i;
                bucket.ChangePending += delegate( Key key, TableChangeArgs args ) {
                                            switch( args.ChangeType ) {
                                                case ChangeType.Add: 
                                                    HandleAddition( i1, key, args );
                                                    break;
                                                case ChangeType.Remove:
                                                case ChangeType.Update:
                                                case ChangeType.Clear:
                                                    throw new NotImplementedException();
                                                default:
                                                    throw new ArgumentOutOfRangeException();
                                            }
                                        };
            }
        }

        private List< UniqueKey > GetUniqueKeys( int sending_bucket_index, Key key1 ) {
            IComparable[] lhs = m_Schema.CreateEmpty();
            List<IComparable[]> unique_keys = new List< IComparable[] >();
            List<NullReport> null_reports = new List< NullReport >();
            NullReport c_report = NullReport.AllPopulated;
            VisitAll( 0, sending_bucket_index,  ref lhs, ref unique_keys, ref null_reports, ref c_report);

            List<UniqueKey> return_set = new List< UniqueKey >();
            Pair<int> bounds;
            m_Schema.GetBounds(sending_bucket_index, out bounds);

            for( int i=0 ; i < unique_keys.Count ; i++ ) {
                IComparable[] key_fields = unique_keys[ i ];
                NullReport null_report = null_reports[ i ];
                if (null_report == NullReport.SomeNull || null_report == NullReport.AllNull) {
                    // skipping null keys
                    continue;
                }

                key1.CopyTo( 0, key_fields, bounds.Left, bounds.Right - bounds.Left );

                UniqueKey key = new UniqueKey(NullReport.AllPopulated, key_fields );
                return_set.Add( key );
            }
            return return_set;
        }

        private void VisitAll(int x, int sending_bucket_index, ref IComparable[] lhs, ref List<IComparable[]> total, ref List<NullReport> null_reports, ref NullReport c_report )
        {
            Console.WriteLine( " VISIT " + x );
            List< TableRow > target_list = m_UnderlyingRowsByBucketIndex[ x ];
            if( x == sending_bucket_index || x == m_ExclusiveIndex ) {
                if( x + 1 < m_UnderlyingRowsByBucketIndex.Count ) {
                    VisitAll( x + 1, sending_bucket_index, ref lhs, ref total, ref null_reports, ref c_report );
                } else {
                    null_reports.Add( c_report );
                    total.Add( lhs );
                }
            } else if( target_list.Count == 0 ) {
                c_report = NullReport.SomeNull;
            } else {
                for( int i = 0 ; i < target_list.Count ; i++ ) {
                    IComparable[] inner_lhs = new IComparable[lhs.Length];
                    lhs.CopyTo( inner_lhs, 0 );

                    Pair< int > bounds;
                    m_Schema.GetBounds( x, out bounds );

                    target_list[ i ].Key.CopyTo( 0, inner_lhs, bounds.Left, bounds.Right - bounds.Left );

                    StringBuilder sb = new StringBuilder();
                    for( int j = 0 ; j < inner_lhs.Length ; j++ ) {
                        sb.Append( " " ).Append( inner_lhs[ j ] ).Append( " " );
                    }

                    Console.WriteLine( "Adding Key Part: " + x + "," + i + ": " + sb );

                    if( x + 1 < m_UnderlyingRowsByBucketIndex.Count ) {
                        VisitAll( x + 1, sending_bucket_index, ref inner_lhs, ref total, ref null_reports, ref c_report );
                    } else {
                        null_reports.Add( c_report );
                        total.Add( inner_lhs );
                    }
                }
            }
        }
        

        private void HandleAddition( int sending_bucket_index, Key index_key, TableChangeArgs changing_rows ) {
            Console.WriteLine("UNDERLYING RECEIVED ADDITION '" + m_ExclusiveIndex + "': " + changing_rows.ItemCount );
            foreach (TableChangeItem change_item in changing_rows.ChangedItems) {
                Console.WriteLine("Subset '" + m_ExclusiveIndex + "' Underlying Addition:" + change_item.NewValue.Key );
                List< UniqueKey > unique_keys = GetUniqueKeys( sending_bucket_index, change_item.NewValue.Key );

                foreach (UniqueKey key in unique_keys)
                {
                    Console.WriteLine("Subset '" + m_ExclusiveIndex + "' Key added : " + key);
                    SubsetKeys.Add( key );
                    FireUnderlyingAdd( key );
                }
                m_UnderlyingRowsByBucketIndex[sending_bucket_index].Add(change_item.NewValue);
            }
            Console.WriteLine(
                "UNDER END" );
        }


        private void FireUnderlyingAdd( SubsetKey key) {
            if( UnderlyingKeyAdded != null ) UnderlyingKeyAdded( key );
        }

        public event SubsetKeyAddedHandler UnderlyingKeyAdded;

        public void AddUniqueKey( UniqueKey addition ) {

            SubsetKeys.Add( ( addition ) );
        }
    }
}